
// 定义状态常量 目的是为了更好的复用
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  constructor(executor) {
    try {
      // 尝试执行这个执行器 如果这个执行器发生错误 他会被catch捕获
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e) // 传递错误原因
    }
  }
  // promise 状态 默认是等待
  status = PENDING
  // 每一个promise对象都有一个成功之后的值和失败后的原因 定义成实例的属性
  // 成功之后的值  
  value = undefined
  // 失败后的原因
  reason = undefined
  // 成功回调 数组可以同时存放多个回调函数
  successCallback = []
  // 失败回调 数组可以同时存放多个回调函数
  failCallback = []
  // value是使用的时候resolve传递过来的参数 表示成功之后的值
  resolve = (value) => { // 箭头函数的目的是为了让这个resolve的this指向这个promise的实例对象 因为调用的时候 是 resolve() 如果不是箭头函数 resolve的this指向的是window
    // 如果状态不是等待 阻止程序向下执行 因为状态一旦变化 就不能在更改
    if (this.status !== PENDING) return
    // 将状态更改为成功
    this.status = FULFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调是否存在 如果存在 调用
    // this.successCallback && this.successCallback(this.value)
    // 循环数组 当数组的长度不是0的时候就代表有callback
    while (this.successCallback.length) {
      // 回调函数要从前向后调用
      this.successCallback.shift()() // 小括号直接调用
    }
  }
  // reason是使用的时候reject传递过来的参数 表示失败之后的原因
  reject = (reason) => {
    // 如果状态不是等待 阻止程序向下执行 因为状态一旦变化 就不能在更改
    if (this.status !== PENDING) return
    // 将状态改为失败
    this.status = REJECTED
    // 保存失败之后的原因
    this.reason = reason
    // 判断失败回调是否存在
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      this.failCallback.shift()()
    }
  }
  then (successCallback, failCallback) { // 如果是成功调用successCallback 如果是失败调用failCallback
    // .then().then()状态传递处理
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => { throw reason }
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          // 捕获回调函数的错误
          try {
            // 如果使用的时候then方法里面return 的是一个promise对象的话 我们要查看这个promise对象的状态 如果状态是成功 调用resolve方法 将成功的结果返回给promise对象 如果是失败的通过reject吧结果返回给promise对象
            let x = successCallback(this.value) // 拿到成功回调的返回值
            // resolve(x)
            // 判断x的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promise返回的结果
            // 再根据返回的结果 决定用resolve还是调用reject 
            // 这个逻辑要在成功的状态做 也要在失败的状态做 在主函数中有一步操作 调用then的pending状态方法的时候也要做 所以要定义一个函数 resolvePromise    promise2是判断回调函数返回的promise对象不能和 then方法返回值相同
            // 但是在这个时候是不能获得promise2的因为 此时的是同步代码 拿不到这个new mypromise 需要把里面的代码变成异步代码使用settimeout 这样这个代码会在所有同步代码执行完成之后才能执行 同步代码执行完成之后就有promise2了
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          // 捕获回调函数的错误
          try {
            // 如果使用的时候then方法里面return 的是一个promise对象的话 我们要查看这个promise对象的状态 如果状态是成功 调用resolve方法 将成功的结果返回给promise对象 如果是失败的通过reject吧结果返回给promise对象
            let x = failCallback(this.reason) // 拿到成功回调的返回值
            // resolve(x)
            // 判断x的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve
            // 如果是promise对象 查看promise返回的结果
            // 再根据返回的结果 决定用resolve还是调用reject 
            // 这个逻辑要在成功的状态做 也要在失败的状态做 在主函数中有一步操作 调用then的pending状态方法的时候也要做 所以要定义一个函数 resolvePromise    promise2是判断回调函数返回的promise对象不能和 then方法返回值相同
            // 但是在这个时候是不能获得promise2的因为 此时的是同步代码 拿不到这个new mypromise 需要把里面的代码变成异步代码使用settimeout 这样这个代码会在所有同步代码执行完成之后才能执行 同步代码执行完成之后就有promise2了
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0);
      } else {
        // 等待
        // 将成功回调和失败回调存储起来 使用的时候可能多次调用then方法 有多个回调函数 将回调函数放入数组中
        // 在处理异步的时候 如果直接放入一个函数是不可以的 需要放一个函数 函数执行的时候里面的回调函数立即执行
        this.successCallback.push(() => {
          setTimeout(() => {
            // 捕获回调函数的错误
            try {
              // 如果使用的时候then方法里面return 的是一个promise对象的话 我们要查看这个promise对象的状态 如果状态是成功 调用resolve方法 将成功的结果返回给promise对象 如果是失败的通过reject吧结果返回给promise对象
              let x = successCallback(this.value) // 拿到成功回调的返回值
              // resolve(x)
              // 判断x的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve
              // 如果是promise对象 查看promise返回的结果
              // 再根据返回的结果 决定用resolve还是调用reject 
              // 这个逻辑要在成功的状态做 也要在失败的状态做 在主函数中有一步操作 调用then的pending状态方法的时候也要做 所以要定义一个函数 resolvePromise    promise2是判断回调函数返回的promise对象不能和 then方法返回值相同
              // 但是在这个时候是不能获得promise2的因为 此时的是同步代码 拿不到这个new mypromise 需要把里面的代码变成异步代码使用settimeout 这样这个代码会在所有同步代码执行完成之后才能执行 同步代码执行完成之后就有promise2了
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            // 捕获回调函数的错误
            try {
              // 如果使用的时候then方法里面return 的是一个promise对象的话 我们要查看这个promise对象的状态 如果状态是成功 调用resolve方法 将成功的结果返回给promise对象 如果是失败的通过reject吧结果返回给promise对象
              let x = failCallback(this.reason) // 拿到成功回调的返回值
              // resolve(x)
              // 判断x的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve
              // 如果是promise对象 查看promise返回的结果
              // 再根据返回的结果 决定用resolve还是调用reject 
              // 这个逻辑要在成功的状态做 也要在失败的状态做 在主函数中有一步操作 调用then的pending状态方法的时候也要做 所以要定义一个函数 resolvePromise    promise2是判断回调函数返回的promise对象不能和 then方法返回值相同
              // 但是在这个时候是不能获得promise2的因为 此时的是同步代码 拿不到这个new mypromise 需要把里面的代码变成异步代码使用settimeout 这样这个代码会在所有同步代码执行完成之后才能执行 同步代码执行完成之后就有promise2了
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0);
        })
      }
    }) // then方法可以链式调用 需要返回一个promise对象

    return promise2
  }
  // 不管这个promise的状态时成功还是失败都要调用这个finally方法的回调函数
  finally (callback) {
    // 得到当前promise的状态 通过then方法 在成功回调和失败回调都调用一下callback 这样无论成功还是失败都能得运行这个callback
    // finally方法还可以继续调用then方法 所以需要返回一个promise对象 而then方法的返回值就是一个promise对象
    // finally方法后面的then方法 还需要拿到当前promise对象的结果
    return this.then((value) => {
      return MyPromise.resolve(callback()).then(() => value)
      // callback()
      // return value
    }, (reason) => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
      // callback()
      // throw reason
    })
  }
  finally1 (callback) {
    return this.then((val) => {
      return MyPromise.resolve(callback()).then(() => val)
    }, (reason) => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
  catch (failCallback) {
    // catch也可以链式调用所以写成then方法
    return this.then(undefined, failCallback)
  }
  // all 方法是一个静态方法 是定义在这个构造函数上的 all方法的返回值是一个promise对象 接受的参数是一个数组
  static all (array) {
    // 声明一个结果数组
    let result = []
    let index = 0 // 任务执行数的计数器
    return new MyPromise((resolve, reject) => {
      // 定义一个方法 这个方法就是将这个值放入数组当中
      function addData (key, value) {
        result[key] = value
        index++
        if (index === array.length) {
          // 判断 index的值是否和array的长度相等 如果相等的话才去调用resolve
          resolve(result)
        }
      }
      // 循环这个数组中的每一项
      for (let i = 0; i < array.length; i++) {
        let current = array[i] // 得到遍历的对象
        // 判断这个对象是普通值还是MyPromise对象
        if (current instanceof MyPromise) {
          // 如果是一个MyPromise对象
          // 首先执行这个MyPromise对象
          // 如果是成功回调直接把这个回调的结果添加到数组里面
          // 如果有一个失败,name整个都失败了 就用reject传递这个失败的原因
          // for循环中间有异步操作 就会不等待异步操作 没有添加上这个i,value  后面调用resolve的时候 异步里面的代码没有执行完 所以结果当中会出现控制
          current.then((value) => addData(i, value), (reason) => reject(reason))
        } else {
          // 普通值
          // 把下标为i的值添加到数组中
          addData(i, array[i])
        }
      }
      // // 循环完之后 代表没有一个是失败的 就把结果数组resolve出去
      // resolve(result)
    })
  }
  static resolve (value) {
    // 判断传入的参数是一个普通值还是promise对象
    if (value instanceof MyPromise) {
      // 如果是一个promise对象 那么就直接返回这个promise对象就可以了
      return value
    } else {
      // 如果不是一个promise对象就是一个普通值 就要用一个promise对象包裹
      return new MyPromise(resolve => resolve(value))
    }
  }
  static race (array) {
    // 传递进来一个数组 返回一个promise对象
    return new MyPromise((resolve, reject) => {
      array.forEach(p => {// 遍历这个promise对象
        // 将每一项转成promise对象 调用then方法 如果是成功 就调用resolve方法 如果是失败 就调用reject方法
        MyPromise.resolve(p).then(val => {
          resolve(val)
        }, reason => {
          reject(reason)
        })
      })
    })
  }
  static allSettled = function (array) {
    let result = []
    return new MyPromise((resolve, reject) => {
      array.forEach((p, i) => {
        MyPromise.resolve(p).then((val) => {
          result.push({
            status: 'fulfilled',
            value: val
          })
          if (result.length === array.length) {
            resolve(result)
          }
        }, (reason) => {
          result.push({
            status: 'rejected',
            reason: reason
          })
          if (result.length === array.length) {
            resolve(result)
          }
        })
      })
    })
  }
  static any (array) {
    // 空数组或者所有 Promise 都是 rejected，则返回状态是 rejected 的新 Promsie，且值为 AggregateError 的错误
    // 只要有一个是 fulfilled 状态的，则返回第一个是 fulfilled 的新实例；
    // 其他情况都会返回一个 pending 的新实例；
    let index = 0
    return new MyPromise((resolve, reject) => {
      if (array.length === 0) return reject(new AggregateError('All promises were rejected'))
      array.forEach((p, i) => {
        MyPromise.resolve(p).then(val => {
          resolve(val)
        }, reason => {
          index++
          // console.log(index);
          // console.log(array.length);
          if (index === array.length) {
            console.log('reject');
            reject(new AggregateError('All promises were rejected'))
          }
        })
      })
    })
  }
}

function resolvePromise (promise2, x, resolve, reject) {
  // 如果then方法返回的promise 和 成功回调函数返回的promise相等 就是自己返回了自己 就需要报错
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) // 组织代码向下运行 return 不写return 还是会执行后面的x.then 因为x的状态并没有改变 这个是改变的promise2的状态
  }
  // 判断x是一个普通值还是promise对象
  if (x instanceof MyPromise) {
    // promise 对象
    // 如果x是promise对象 需要调用promise的then方法查看promise的状态
    // 如果是成功的会调用then的第一个回调函数 如果是失败的会调用then的第二个回调函数
    x.then((value) => {
      resolve(value)
    }, (reason) => {
      reject(reason)
    }) // 这个代码可以简化为以下的代码
    // x.then(resolve,reject)
  } else {
    // 普通值
    resolve(x) // 如果是普通值 就直接resolve传递给下一个promise对象就可以了
  }
}

module.exports = MyPromise